/*
 *
 * Copyright (c) 2005 The Exceoon Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Exceoon Software Foundation (http://www.exceoon.com/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. Products derived from this software may not be called " Exceoon ",
 *    nor may " Exceoon " appear in their name, without prior written
 *    permission of the Exceoon Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * Exceoon Software Corporation and was originally based on software
 * Exceoon copyright (c) 2005, International
 * Business Machines, Inc., http://www.Exceoon.com.  For more
 * information on the Exceoon Software Foundation, please see
 * <http://www.Exceoon.com/>.
 */

package com.exceoon.equals.formula;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.exceoon.equals.extend.function.ABSFunction;
import com.exceoon.equals.extend.function.AVGFunction;
import com.exceoon.equals.extend.function.EMAFunction;
import com.exceoon.equals.extend.function.EXPMEMAFunction;
import com.exceoon.equals.extend.function.GetFiledFunction;
import com.exceoon.equals.extend.function.IFFunction;
import com.exceoon.equals.extend.function.MAFunction;
import com.exceoon.equals.extend.function.MAXFunction;
import com.exceoon.equals.extend.function.MINFunction;
import com.exceoon.equals.extend.function.REFFunction;
import com.exceoon.equals.extend.operator.OperatorArrayDivRealImpl;
import com.exceoon.equals.extend.operator.OperatorArrayMinusImpl;
import com.exceoon.equals.extend.operator.OperatorArrayMulImpl;
import com.exceoon.equals.extend.operator.OperatorArrayMulRealImpl;
import com.exceoon.equals.extend.operator.OperatorConcatImpl;
import com.exceoon.equals.extend.operator.OperatorEqualImpl;
import com.exceoon.equals.extend.operator.OperatorLessImpl;
import com.exceoon.equals.extend.operator.OperatorMoreImpl;
import com.exceoon.equals.extend.operator.OperatorNotImpl;
import com.exceoon.equals.extend.operator.OperatorRealMulArrayImpl;
import com.exceoon.equals.extend.operator.OperatorZhengChuImpl;
import com.exceoon.equals.formula.support.DefinedVariableExpressionElement;
import com.exceoon.equals.formula.support.FieldExpressionElement;

import fr.expression4j.basic.OperatorManager;
import fr.expression4j.basic.impl.ComplexOrRealExpressionElement;
import fr.expression4j.basic.impl.ConstantOrVariableExpressionElement;
import fr.expression4j.basic.impl.FunctionExpressionElement;
import fr.expression4j.basic.impl.ParenthesisExpressionElement;
import fr.expression4j.core.Catalog;
import fr.expression4j.core.Expression;
import fr.expression4j.core.ExpressionModel;
import fr.expression4j.core.Parameters;
import fr.expression4j.core.exception.ParsingException;
import fr.expression4j.factory.ExpressionFactory;
import fr.expression4j.factory.ExpressionModelFactory;
import fr.expression4j.factory.OperatorFactory;
import fr.expression4j.factory.OperatorManagerFactory;
import fr.expression4j.sample.custom.element.StringExpressionElement;

/**
 * Author	David.Liu 
 * Mail		david.liu@exceoon.com
 * copyright	Exceoon corporation
 */

public class ExpressionUtils
{
	static
	{

		try
		{
			
			// create a specific expression model
			// ExpressionModel customOperatorExpressionModel =
			// ExpressionModelFactory.createExpressionModel("CustomOperatorExpressionModel");
			ExpressionModel customOperatorExpressionModel = ExpressionModelFactory
			        .getDefaultExpressionModel();

			customOperatorExpressionModel.addUnaryOperator(OperatorFactory
			        .getOperator("UnaryMinus"));
			customOperatorExpressionModel.addUnaryOperator(OperatorFactory
			        .getOperator("UnaryPlus"));

			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .getOperator("Plus"), 3);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .getOperator("Minus"), 3);

			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .getOperator("Multiply"), 4);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .getOperator("Divide"), 4);

			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .getOperator("Pow"), 5);

			customOperatorExpressionModel.addExpressionElement(
			        new ComplexOrRealExpressionElement(), 1);
			customOperatorExpressionModel.addExpressionElement(
			        new FunctionExpressionElement(), 2);
			customOperatorExpressionModel.addExpressionElement(
			        new ConstantOrVariableExpressionElement(), 3);
			customOperatorExpressionModel.addExpressionElement(
			        new ParenthesisExpressionElement(), 5);

			// OperatorManager supOperatorManager =
			// OperatorManagerFactory.createOperatorManager("supOperatorManager");
			OperatorManager supOperatorManager = OperatorManagerFactory
			        .getDefaultOperatorManager();

			// create the new operator
			// add binary operator supported by the specific expression model
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("mod", "%", false), 4);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("zhengchu", "$", false), 4);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("less", "<", false), 2);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("more", ">", false), 2);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("equal", "==", false), 2);
			customOperatorExpressionModel.addBinaryOperator(OperatorFactory
			        .createOperator("concat", "||", false), 2);
			customOperatorExpressionModel.addUnaryOperator(OperatorFactory
			        .createOperator("not", "!", true));

			// add operator specific to previous model

			supOperatorManager.addOperatorImpl(new OperatorZhengChuImpl());
			supOperatorManager.addOperatorImpl(new OperatorLessImpl());
			supOperatorManager.addOperatorImpl(new OperatorMoreImpl());
			supOperatorManager.addOperatorImpl(new OperatorEqualImpl());
			supOperatorManager.addOperatorImpl(new OperatorNotImpl());
			supOperatorManager.addOperatorImpl(new OperatorConcatImpl());

			supOperatorManager.addOperatorImpl(new OperatorArrayMinusImpl());
			supOperatorManager.addOperatorImpl(new OperatorArrayMulImpl());
			supOperatorManager.addOperatorImpl(new OperatorArrayMulRealImpl());
			supOperatorManager.addOperatorImpl(new OperatorRealMulArrayImpl());
			supOperatorManager.addOperatorImpl(new OperatorArrayDivRealImpl());

			Catalog catalog = ExpressionFactory.getCatalog();
			catalog.addExpression(new GetFiledFunction());
			catalog.addExpression(new EMAFunction());
			catalog.addExpression(new MAFunction());
			catalog.addExpression(new AVGFunction());
			catalog.addExpression(new MAXFunction());
			catalog.addExpression(new MINFunction());
			catalog.addExpression(new ABSFunction());
			catalog.addExpression(new IFFunction());
			catalog.addExpression(new REFFunction());
			catalog.addExpression(new EXPMEMAFunction());

			customOperatorExpressionModel.addExpressionElement(
			        new StringExpressionElement(), 1);

			customOperatorExpressionModel.addExpressionElement(
			        new FieldExpressionElement(), 4);
			customOperatorExpressionModel.addExpressionElement(
			        new DefinedVariableExpressionElement(), 4);

		} catch (Exception e)
		{
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	public static Parameters createParameters()
	{
		return ExpressionFactory.createParameters();
	}

	public static Expression createExpression(String expression)
	        throws ParsingException
	{
		return ExpressionFactory.createExpression(expression);
	}

	public static <T> List<T> array2list(T[] obj)
	{
		return Arrays.asList(obj);
	}

	public static <T> List<Double> array2Doublelist(T[] obj)
	{
		ArrayList<Double> rtn = new ArrayList<Double>(obj.length);

		for (T o : obj)
		{
			rtn.add(Double.parseDouble(String.valueOf(o)));
		}

		return rtn;
	}
}
